package com.yyaccp.easybuy.security.util;

import com.yyaccp.easybuy.security.vo.SnsPlatform;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class SnsLoginTemplate {
    private RestTemplate restTemplate;
    private SnsPlatform snsPlatform;

    public SnsLoginTemplate(SnsPlatform snsPlatform) {
        this.restTemplate = new RestTemplate();
        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        interceptors.add((httpRequest, bytes, clientHttpRequestExecution) -> {
            // 码云需要设置user-agent和accept才能获取token
            httpRequest.getHeaders().set("Accept", "application/json;charset=UTF-8");
            httpRequest.getHeaders().set("User-Agent","Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0");
            return clientHttpRequestExecution.execute(httpRequest, bytes);
        });
        restTemplate.setInterceptors(interceptors);
        this.snsPlatform = snsPlatform;
    }

    /**
     * 获取授权码
     * @param response
     * @throws Exception
     */
    public void authorize(HttpServletResponse response) throws Exception{
        response.sendRedirect(SocialUtils.getAuthorizeUrl(snsPlatform));
    }

    /**
     * 根据授权码获取access_token
     * @param code
     * @param state
     * @return
     */
    public String getAccessToken(String code, String state) {
        String accessTokenUrl = SocialUtils.getAccessTokenUrl(snsPlatform);
        Map<String, String> paramsMap = SocialUtils.getAccessTokenParams(snsPlatform, code, state);
        // 通过http访问获取accessToken的网址
        Map<String, String> returnMap = restTemplate.postForObject(accessTokenUrl, paramsMap, Map.class);
        if (log.isDebugEnabled()) {
            log.debug("callback response result:{}", returnMap);
        }
        // 第三步，通过access_token获取用户信息
        // 如果是微信登录，已经返回了accessToken和openId
        // 如果是QQ登录，还需要使用accessToken获取openId
        // 再使用openId和accessToken和app秘钥获取用户信息
        return returnMap.get("access_token");
    }

    public String getOpenId(String accessToken) {
        return "not impl";
    }
    /**
     * 根据access_token获取用户信息
     * @param accessToken
     * @return
     */
    public Map<String, String> getUserInfo(String accessToken) {
        String url = SocialUtils.getUserInfoUrl(snsPlatform, accessToken);
        // github需要在请求头中传递token
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
        HttpEntity<String> requestEntity = new HttpEntity<>(null, headers);
        // 通过http访问获取用户信息的网址
        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Map.class);
        Map<String, String> resultMap = responseEntity.getBody();
        if (log.isDebugEnabled()) {
            log.debug("request userInfo api result:{}", resultMap);
        }
        return resultMap;
    }

    @Slf4j
    private static class SocialUtils {
        public static String getAuthorizeUrl(SnsPlatform platform) {
            String result;
            try {
                StringBuilder builder = new StringBuilder(platform.getServerUrl())
                        .append(platform.getAuthorizePath())
                        .append("?")
                        // 在服务器上申请的app id
                        .append("client_id=").append(platform.getClientId())
                        // 在服务器上申请的app id,钉钉登录参数名字为appid
                        .append("&appid=").append(platform.getClientId())
                        // 回调地址，要跟在服务器上设置的回调地址一致
                        .append("&redirect_uri=").append(URLEncoder.encode(platform.getCallbackUrl(), "UTF-8"))
                        // 告诉服务器，我要请求授权码
                        .append("&response_type=code");
                // 钉钉登录需要
                if(platform.getPlatformName().equals("dingtalk")) {
                    builder.append("&scope=snsapi_login");
                }
                // qq登录需要
                if(platform.getPlatformName().equals("qq")) {
                    builder.append("&state=state");
                }
                result = builder.toString();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                result = "";
            }
            log.debug("AuthorizeUrl:{}", result);
            return result;
        }

        public static Map<String, String> getAccessTokenParams(SnsPlatform platform, String code, String state) {
            Map<String, String> paramsMap = new HashMap<>();
            paramsMap.put("grant_type", "authorization_code");
            // 授权码
            paramsMap.put("code", code);
            // app id
            paramsMap.put("client_id", platform.getClientId());
            // 回调地址
            paramsMap.put("redirect_uri", platform.getCallbackUrl());
            // app密码
            paramsMap.put("client_secret", platform.getClientSecret());
            // state
            paramsMap.put("state", state);
            // qq登录指定返回值类型格式为json
            paramsMap.put("fmt", "json");
            return paramsMap;
        }

        public static String getAccessTokenUrl(SnsPlatform platform) {
            return new StringBuffer(platform.getServerUrl()).append(platform.getTokenPath()).toString();
        }

        public static String getUserInfoUrl(SnsPlatform platform, String accessToken) {
            return new StringBuffer(platform.getUserInfoUrl()).append("?access_token=").append(accessToken).toString();
        }
    }
}
